#ifndef IWINDOW_H_
#define IWINDOW_H_
#include "def.h"

//The class IWindow responsible for geometry
class IWindow {
    public:
        IWindow()
        {
            m_min_h = 0;
            m_min_w = 0;
            m_y = 0;
            m_x = 0;
            m_h = 0;
            m_w = 0;
            m_enabled = false;
            m_out_of_range = false;
            m_parent = NULL;
        }
        virtual ~IWindow()
        {
        }
        //Print actual data of the buffer on the window

        virtual int Display(int focus) = 0;
        //Updates screen buffer
        virtual void Update() = 0;
        //Does actual paint on screen
        virtual void Paint() = 0;
        virtual void Clean() = 0;
        virtual void Top() = 0;

        virtual void AddWindow(IWindow* win)
        {
            if(win){
                win->m_parent = this;
            }
        }

        virtual void RemoveWindow(IWindow* win)
        {
            if(win->m_parent == this)
            {
                win->m_parent = NULL;
            }
        }

        virtual void Locate(int y, int x, int h, int w)
        {
            //if terminal size becomes too small for the window 
            //disable it till it becomes enough to show the window
            if(g_term.lines() < GetMinH() || g_term.cols() < GetMinW()){
                Disable();
                m_out_of_range = true;
            } else {
                //Enable the window because it becomes enough size
                if(m_out_of_range){
                    Enable();
                    m_out_of_range = false;
                }
                //Here we check the new window size;
                h = (h > GetMinH())? h : GetMinH();
                w = (w > GetMinW())? w : GetMinW();
                //Check if the window run out of the terminal size
                if(y >= 0 && y + h <= g_term.lines()){
                    m_y = y;
                    m_h = h;
                }
                if(x >= 0 && x + w <= g_term.cols()){
                    m_x = x;
                    m_w = w;
                }
            }
        }

        virtual void Resize_LT(int x, int y)
        {
            if(m_parent == NULL){
                //if terminal size becomes too small for the window 
                //disable it till it becomes enough to show the window
                if(g_term.lines() < GetMinH() || g_term.cols() < GetMinW()){
                    Disable();
                    m_out_of_range = true;
                } else {
                    int new_w = m_w - x;
                    int new_h = m_h - y;
                    int new_x = m_x + x;
                    int new_y = m_y + y;
                    //Validate new size and new position
                    if((new_h < GetMinH()) || (new_h + new_y > g_term.lines() ) || (new_y < 0)){
                        new_h = m_h;
                        new_y = m_y;
                    }
                    if((new_w < GetMinW()) || (new_w + new_x > g_term.cols() ) || (new_x < 0)){
                        new_w = m_w;
                        new_x = m_x;
                    }
                    m_w = new_w;
                    m_h = new_h;
                    m_x = new_x;
                    m_y = new_y;
                    //Enable the window because terminal becomes enough size
                    if(m_out_of_range){
                        Enable();
                        m_out_of_range = false;
                    }
                }
            }
        }

        virtual void Resize_RB(int x, int y)
        {
            if(m_parent == NULL){
                //if terminal size becomes too small for the window 
                //disable it till it becomes enough to show the window
                if(g_term.lines() < GetMinH() || g_term.cols() < GetMinW()){
                    Disable();
                    m_out_of_range = true;
                } else {
                    //Calculate new size
                    int new_w = m_w + x;
                    int new_h = m_h + y;
                    //validate if the new size is not less of minmum window size
                    new_w = (new_w < GetMinW())? GetMinW() : new_w;
                    new_h = (new_h < GetMinH())? GetMinH() : new_h;
                    //validate if the new size is not run out of the terminal size
                    if((m_x + new_w) >= g_term.cols()){
                        new_w = g_term.cols() - m_x;
                        //check if the new size is not less of minmum window size
                        if(new_w < GetMinW())
                        {
                            //need to move the window
                            new_w = GetMinW();
                            m_x = g_term.cols() - new_w; 
                        }
                    }
                    if((m_y + new_h) >= g_term.lines()){
                        new_h = g_term.lines() - m_y;
                        //check if the new size is not less of minmum window size
                        if(new_h < GetMinH())
                        {
                            //need to move the window
                            new_h = GetMinH();
                            m_y = g_term.lines() - new_h; 
                        }
                    }
                    m_h = new_h;
                    m_w = new_w;
                    //Enable the window because terminal becomes enough size
                    if(m_out_of_range){
                        Enable();
                        m_out_of_range = false;
                    }
                }
            }
        }

        virtual void Move(int x, int y)
        {
            if(m_parent == NULL){
                //if terminal size becomes too small for the window 
                //disable it till it becomes enough to show the window
                if(g_term.lines() < GetMinH() || g_term.cols() < GetMinW()){
                    Disable();
                    m_out_of_range = true;
                } else {
                    //Calculate new window position
                    int new_x = m_x + x;
                    int new_y = m_y + y;
                    //Check if the new position is not run out of the terminal size
                    m_x = (new_x < 0)? 0 : ((new_x + m_w) < g_term.cols())? new_x : (g_term.cols() - m_w);
                    m_y = (new_y < 0)? 0 : ((new_y + m_h) < g_term.lines())? new_y : (g_term.lines() - m_h);
                    if(m_x < 0){
                        m_x = 0;
                        m_w = g_term.cols();
                    }
                    if(m_y < 0){
                        m_y = 0;
                        m_h = g_term.lines();
                    }
                    //Enable the window because terminal becomes enough size
                    if(m_out_of_range){
                        Enable();
                        m_out_of_range = false;
                    }
                }
            }
        }

        virtual bool IsEnabled()
        {
            return m_enabled;
        }

        virtual void GetGeometry(int &y, int &x, int &h, int &w)
        {
            y = m_y;
            x = m_x;
            h = m_h;
            w = m_w;
        }

        virtual int GetMinH()
        {
            return m_min_h;
        }

        virtual int GetMinW()
        {
            return m_min_w;
        }

        virtual void Enable()
        {
            m_enabled = true;
            if(m_parent != NULL){
                m_parent->Display(true);
            }
        }

        virtual void Disable()
        {
            m_enabled = false;
            if(m_parent != NULL){
                m_parent->Display(true);
            }
        }

    protected:
        bool m_enabled;
        bool m_out_of_range;
        int m_x;
        int m_y;
        int m_w;
        int m_h;
        int m_min_h;
        int m_min_w;
        IWindow* m_parent;
        static NCursesWindow g_term;
};



#endif //IWINDOW_H_

